﻿namespace LogManager.Utils.Unity
{
    using LogManager.Utils.Unity.Rules;
    using Microsoft.Practices.Unity;
    using Microsoft.Practices.Unity.InterceptionExtension;
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public class InterceptOnRegisterExtension : UnityContainerExtension
    {
        public ITypeInterceptor Interceptor { get; private set; }

        private readonly List<IInterceptRule> _rules;
        public ReadOnlyCollection<IInterceptRule> Rules { get; private set; }

        public InterceptOnRegisterExtension()
        {
            Interceptor = new VirtualMethodInterceptor();
            _rules = new List<IInterceptRule>();
            Rules = _rules.AsReadOnly();
        }

        public InterceptOnRegisterExtension AddMatchingRule(IInterceptRule rule)
        {
            _rules.Add(rule);
            return this;
        }

        public InterceptOnRegisterExtension AddNewMatchingRule<T>() where T : IInterceptRule, new()
        {
            _rules.Add(new T());
            return this;
        }

        public InterceptOnRegisterExtension SetDefaultInterceptor<T>() where T : ITypeInterceptor, new()
        {
            Interceptor = new T();
            return this;
        }

        protected override void Initialize()
        {
            AddInterceptionExtensionIfNotExists();

            Context.Registering +=
                (sender, e) => SetInterceptorFor(e.TypeFrom, e.TypeTo ?? e.TypeFrom);
        }

        private void AddInterceptionExtensionIfNotExists()
        {
            if (Container.Configure<Interception>() == null)
            {
                Container.AddNewExtension<Interception>();
            }
        }

        private void SetInterceptorFor(Type typeToIntercept, Type typeOfInstance)
        {
            if (typeToIntercept == null || typeOfInstance == null)
            { return; }

            if (!AllMatchingRulesApply(typeToIntercept, typeOfInstance))
            {
                return;
            }

            if (Interceptor.CanIntercept(typeOfInstance))
            {
                Container
                    .Configure<Interception>()
                    .SetDefaultInterceptorFor(typeOfInstance, Interceptor);
            }
            else if (Interceptor.CanIntercept(typeToIntercept))
            {
                Container
                    .Configure<Interception>()
                    .SetDefaultInterceptorFor(typeToIntercept, Interceptor);
            }
        }

        private bool AllMatchingRulesApply(Type typeToIntercept, Type typeOfInstance)
        {
            foreach (var rule in _rules)
            {
                if (!rule.Matches(typeToIntercept, typeOfInstance))
                {
                    return false;
                }
            }

            return true;
        }
    }
}
